package com.fansl.allround.daemon.quartz.jobs;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.fansl.allround.common.core.constant.enums.ScheduleJobLogStatusEnum;
import com.fansl.allround.common.core.constant.enums.ScheduleJobStatusEnum;
import com.fansl.allround.common.core.util.SpringContextHolder;
import com.fansl.allround.daemon.quartz.config.ScheduleConstants;
import com.fansl.allround.daemon.quartz.entity.ScheduleJob;
import com.fansl.allround.daemon.quartz.entity.ScheduleJobLog;
import com.fansl.allround.daemon.quartz.service.ScheduleJobService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.CronExpression;
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.ParseException;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author fansl
 * @Description: Abstract Quartz Job
 * @date 2020/1/11 14:39
 */
@Slf4j
public abstract class AbstractQuartzJob implements Job {
    private static ThreadLocal<LocalDateTime> threadLocal = new ThreadLocal<>();

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        ScheduleJob scheduleJob = (ScheduleJob) context.getMergedJobDataMap()
                .get(ScheduleConstants.JOB_PROPERTIES_KEY);
        try {
            executeBefore(context, scheduleJob);
            if (ObjectUtil.isNotNull(scheduleJob)) {
                doExecute(context, scheduleJob);
            }
            executeAfter(context, scheduleJob, null);
        } catch (Exception e) {
            log.error("任务执行失败，任务ID:{}", scheduleJob.getJobId(), e);
            executeAfter(context, scheduleJob, e);
            throw new JobExecutionException(e);
        }
    }

    /**
     * 执行前
     *
     * @param context
     * @param scheduleJob
     */
    protected void executeBefore(JobExecutionContext context, ScheduleJob scheduleJob) {
        threadLocal.set(LocalDateTime.now());
    }

    protected void executeAfter(JobExecutionContext context, ScheduleJob scheduleJob, Exception e) {
        LocalDateTime startTime = threadLocal.get();
        LocalDateTime endTime = LocalDateTime.now();
        threadLocal.remove();

        final ScheduleJobLog scheduleJobLog = new ScheduleJobLog();
        scheduleJobLog.setJobId(scheduleJob.getJobId());
        scheduleJobLog.setJobName(scheduleJob.getJobName());
        scheduleJobLog.setJobGroup(scheduleJob.getJobGroup());
        scheduleJobLog.setMethodName(scheduleJob.getMethodName());
        scheduleJobLog.setMethodParams(scheduleJob.getMethodParams());
        scheduleJobLog.setStartTime(startTime);
        scheduleJobLog.setEndTime(endTime);
        long runTimes = Duration.between(startTime, endTime).toMillis();
        scheduleJobLog.setTimes(runTimes);
        scheduleJobLog.setJobMessage(scheduleJob.getJobName() + " 总共耗时：" + runTimes + "毫秒");

        if (ObjectUtil.isNull(e)) {
            scheduleJobLog.setStatus(ScheduleJobLogStatusEnum.SUCCESS.getCode());
        } else {
            scheduleJobLog.setStatus(ScheduleJobLogStatusEnum.FAIL.getCode());
            scheduleJobLog.setExceptionInfo(ExceptionUtil.getMessage(e));
        }
        //持久化
        ScheduleJobService scheduleJobService =
                SpringContextHolder.getBean(ScheduleJobService.class);
        scheduleJobService.saveJobLog(scheduleJobLog);
        //
        ScheduleJob update = new ScheduleJob();
        update.setJobId(scheduleJob.getJobId());
        try {
            CronExpression cronExpression = new CronExpression(scheduleJob.getCronExpression());
            if( cronExpression.getNextValidTimeAfter(new Date())!=null){
                update.setStatus(ScheduleJobStatusEnum.RUNNING.getCode());
            }else {
                update.setStatus(ScheduleJobStatusEnum.COMPLETED.getCode());
            }
        } catch (ParseException e1) {
            e1.printStackTrace();
        }
        scheduleJobService.updateExecuteDetail(scheduleJob.getJobId(),update);
    }

    /**
     * 执行方法，由子类重载
     *
     * @param context     工作执行上下文对象
     * @param scheduleJob 系统计划任务
     * @throws Exception 执行过程中的异常
     */
    protected abstract void doExecute(JobExecutionContext context, ScheduleJob scheduleJob) throws Exception;
}
